package org.zoomdev.zoom.aop.impl;

import org.objectweb.asm.*;
import org.objectweb.asm.commons.GeneratorAdapter;
import org.zoomdev.zoom.aop.ProxyFactory;
import org.zoomdev.zoom.aop.ProxyObject;
import org.zoomdev.zoom.aop.factory.impl.AsmClassloader;
import org.zoomdev.zoom.aop.utils.AsmUtils;
import org.zoomdev.zoom.common.exceptions.ZoomException;
import org.zoomdev.zoom.common.utils.Classes;
import org.zoomdev.zoom.common.utils.Creators;

import java.lang.reflect.Method;
import java.util.List;

import static org.objectweb.asm.Opcodes.*;
import static org.objectweb.asm.Type.getInternalName;

public final class AsmProxyFactory implements ProxyFactory {


    private static final Type METHOD_TYPE = Type.getType(java.lang.reflect.Method.class);
    private static final Type PROXY_OBJECT_TYPE = Type.getType(ProxyObject.class);



//    @Override
//    public <T> T createDecoration(Class<T> type, T target) {
//        return createObject(type,new DecorationObject<>(target));
//    }
//    private static void addStaticMethodFields(ClassWriter cw, Class<?> interfaceClass, String className, List<Method> methods) {
//        // 生成静态代码块 <clinit>
//        MethodVisitor clinitMv = cw.visitMethod(Opcodes.ACC_STATIC, "<clinit>", "()V", null, null);
//        clinitMv.visitCode();
//
//        for (int i = 0; i < methods.size(); i++) {
//            java.lang.reflect.Method method = methods.get(i);
//
//            // 生成字段定义: static final Method $_method_N;
//            cw.visitField(
//                    Opcodes.ACC_STATIC | Opcodes.ACC_FINAL | ACC_PUBLIC,
//                    "$_method_" + i,
//                    METHOD_TYPE.getDescriptor(),
//                    null, null
//            ).visitEnd();
//
//            // 生成字段初始化代码:
//            // $_method_N = A.class.getDeclaredMethod("methodName", paramTypes);
//            clinitMv.visitLdcInsn(Type.getType(interfaceClass));                // 加载 A.class
//            clinitMv.visitLdcInsn(method.getName());                            // 方法名
//            clinitMv.visitIntInsn(Opcodes.BIPUSH, method.getParameterCount()); // 参数数量
//            clinitMv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Class");      // 创建Class数组
//
//            Class<?>[] paramTypes = method.getParameterTypes();
//            for (int j = 0; j < paramTypes.length; j++) {
//                clinitMv.visitInsn(Opcodes.DUP);                               // 复制数组引用
//                clinitMv.visitIntInsn(Opcodes.BIPUSH, j);                      // 数组索引
//                Class<?> paramType = paramTypes[j];
//                if (paramType.isPrimitive()) {
//                    // 生成 java.lang.Integer.TYPE 等基本类型 Class 访问
//                    String wrapperType = getPrimitiveWrapperType(paramType);
//                    clinitMv.visitFieldInsn(
//                            Opcodes.GETSTATIC,
//                            "java/lang/" + wrapperType,
//                            "TYPE",
//                            "Ljava/lang/Class;"
//                    );
//                } else {
//                    clinitMv.visitLdcInsn(Type.getType(paramType));
//                }
//                //clinitMv.visitLdcInsn(Type.getType(paramTypes[j]));            // 参数类型
//                clinitMv.visitInsn(Opcodes.AASTORE);                           // 存储到数组
//            }
//
//            clinitMv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
//                    "java/lang/Class",
//                    "getDeclaredMethod",
//                    "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;",
//                    false);
//
//            clinitMv.visitFieldInsn(Opcodes.PUTSTATIC,
//                    className,
//                    "$_method_" + i,
//                    METHOD_TYPE.getDescriptor());
//        }
//
//        clinitMv.visitInsn(Opcodes.RETURN);
//        clinitMv.visitMaxs(0, 0); // 自动计算
//        clinitMv.visitEnd();
//    }

    public <T,C> T createObject(Class<T> interfaceOrAbstractClass, ProxyObject<C> proxyObject) {
        Class<?> enhanced = createClass(interfaceOrAbstractClass);
        Object instance = Creators.newInstance(enhanced);
        Classes.set(enhanced, instance, "$_obj", proxyObject);
        return (T) instance;
    }

    public Class<?> createImpl(Class<?> interfaceOrAbstractClass, String orgClassName) {
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
        String className = AsmUtils.makeExternalName(orgClassName);// generateClassName(interfaceOrAbstractClass);
        String superName = getSuperName(interfaceOrAbstractClass);

        // 定义类结构
        cw.visit(AsmUtils.VERSION, ACC_PUBLIC, className, null, superName, getInterfaces(interfaceOrAbstractClass));


        List<Method> methods = ProxyFactory.getProxyMethods(interfaceOrAbstractClass);
        // 添加字段
        addProxyObjectField(cw);
        addMethodFields(cw, interfaceOrAbstractClass, methods);

        // 添加静态初始化块
        addStaticMethodFields(cw, interfaceOrAbstractClass, className, methods);

        // 添加构造函数
        AsmUtils.generateConstructor(cw, superName);


        // 添加方法实现
        addMethodImplementation(cw, methods, interfaceOrAbstractClass);
        cw.visitEnd();

        // 加载类

        Class<?> type = AsmClassloader.defineClass(orgClassName, cw.toByteArray());
        initClass(type, methods);
        return type;
    }

    private String getSuperName(Class<?> clazz) {
        return clazz.isInterface() ? getInternalName(Object.class) : getInternalName(clazz);
    }

    private String[] getInterfaces(Class<?> clazz) {
        return clazz.isInterface() ? new String[]{getInternalName(clazz)} : null;
    }



    private void addProxyObjectField(ClassVisitor cv) {
        AsmUtils.addField(cv, ACC_PUBLIC, "$_obj", ProxyObject.class);
    }

    private void addMethodFields(ClassVisitor cv, Class<?> clazz, List<Method> methods) {
        for (int index = 0, c = methods.size(); index < c; ++index) {
            // 配置字段 $_cfg_n
            AsmUtils.addField(cv, ACC_PROTECTED, "$_cfg_" + index, Object.class);
        }
    }

    private static void addStaticMethodFields(ClassWriter cw, Class<?> interfaceClass, String className, List<Method> methods) {
        for (int i = 0; i < methods.size(); i++) {
            java.lang.reflect.Method method = methods.get(i);

            // 生成字段定义: static final Method $_method_N;
            cw.visitField(Opcodes.ACC_STATIC | ACC_PRIVATE, "$_method_" + i,
                    METHOD_TYPE.getDescriptor(), null, null
            ).visitEnd();
        }
    }

    private void addMethodImplementation(ClassWriter cw, List<Method> methods, Class<?> interfaceClass) {

        for (int methodIndex = 0, c = methods.size(); methodIndex < c; ++methodIndex) {
            Method method = methods.get(methodIndex);
            addMethodImplementation(cw, method, methodIndex, interfaceClass);
        }

    }

    private void initClass(Class<?> enhanced, List<Method> methods) {
        for (int index = 0, c = methods.size(); index < c; ++index) {
            try {
                Classes.set(enhanced, "$_method_" + index, methods.get(index));
            } catch (Exception e) {
                throw new ZoomException(e);
            }
        }
    }

    private static void addMethodImplementation(
            ClassWriter cw,
            Method method,
            int methodIndex,
            Class<?> interfaceClass
    ) {
        addCreateMethod(cw, methodIndex, interfaceClass);
        addInterfaceMethod(cw, method, methodIndex, interfaceClass);
    }

    /**
     *  Object create_1(){
     *      synchronized($_method_1){
     *          if($_cfg_1==null){
     *              $_cfg_1=$_obj.create(Interface.class,this,$_method_1)
     *          }
     *          return $_cfg_1
     *      }
     *  }
     */
    private static void addCreateMethod(
            ClassWriter cw,
            int methodIndex,
            Class<?> interfaceClass
    ) {
        String className = interfaceClass.getName().replace('.', '/') + "$Proxy";

        MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PRIVATE, "create_" + methodIndex, "()Ljava/lang/Object;", null, null);
        mv.visitCode();

        ////////////////////////////////
        // 1. 同步块开始
        ////////////////////////////////
        Label syncStart = new Label();
        Label syncEnd = new Label();
        Label exceptionHandler = new Label();

        // 获取 $_method_n
        mv.visitFieldInsn(
                Opcodes.GETSTATIC,
                className,
                "$_method_" + methodIndex,
                "Ljava/lang/reflect/Method;"
        );
        mv.visitInsn(Opcodes.DUP);
        mv.visitVarInsn(Opcodes.ASTORE, 1); // 存储锁对象到局部变量1

        // MONITORENTER
        mv.visitLabel(syncStart);
        mv.visitInsn(Opcodes.MONITORENTER);

        // 双重检查锁定
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitFieldInsn(
                Opcodes.GETFIELD,
                className,
                "$_cfg_" + methodIndex,
                "Ljava/lang/Object;"
        );
        Label notNullLabel = new Label();
        mv.visitJumpInsn(Opcodes.IFNONNULL, notNullLabel);

        ////////////////////////////////
        // 2. 初始化 $_cfg_n
        ////////////////////////////////
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitFieldInsn(
                Opcodes.GETFIELD,
                className,
                "$_obj",
                "Lorg/zoomdev/zoom/aop/ProxyObject;"
        );
        mv.visitLdcInsn(Type.getType("L" + Type.getInternalName(interfaceClass) + ";"));
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitFieldInsn(
                Opcodes.GETSTATIC,
                className,
                "$_method_" + methodIndex,
                "Ljava/lang/reflect/Method;"
        );
        mv.visitMethodInsn(
                Opcodes.INVOKEINTERFACE,
                "org/zoomdev/zoom/aop/ProxyObject",
                "create",
                "(Ljava/lang/Class;Ljava/lang/Object;Ljava/lang/reflect/Method;)Ljava/lang/Object;",
                true
        );

        // 保存到 $_cfg_n
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitInsn(Opcodes.SWAP); // 交换栈顶元素（this 和 $_cfg_n 值）
        mv.visitFieldInsn(
                Opcodes.PUTFIELD,
                className,
                "$_cfg_" + methodIndex,
                "Ljava/lang/Object;"
        );

        ////////////////////////////////
        // 3. 退出同步块
        ////////////////////////////////
        mv.visitLabel(notNullLabel);
        mv.visitVarInsn(Opcodes.ALOAD, 1); // 加载锁对象
        mv.visitInsn(Opcodes.MONITOREXIT);
        mv.visitLabel(syncEnd);

        // return $_cfg_n
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitFieldInsn(Opcodes.GETFIELD, className, "$_cfg_" + methodIndex, "Ljava/lang/Object;");
        mv.visitInsn(Opcodes.ARETURN);

        ////////////////////////////////
        // 4. 异常处理块
        ////////////////////////////////
        mv.visitLabel(exceptionHandler);
        mv.visitFrame(
                Opcodes.F_FULL,
                2,
                new Object[]{className, "java/lang/Object"},
                1,
                new Object[]{"java/lang/Throwable"}
        );
        mv.visitVarInsn(Opcodes.ALOAD, 1); // 加载锁对象
        mv.visitInsn(Opcodes.MONITOREXIT);
        mv.visitInsn(Opcodes.ATHROW);

        // 设置 try-catch 块范围
        mv.visitTryCatchBlock(syncStart, syncEnd, exceptionHandler, null);

        // 设置最大栈和局部变量（由 ASM 自动计算）
        mv.visitMaxs(0, 0);
        mv.visitEnd();
    }

    private static void addInterfaceMethod(
            ClassWriter cw,
            Method m,
            int index,
            Class<?> interfaceClass
    ) {
        Type returnType = Type.getType(m.getReturnType());
        Type[] paramTypes = Type.getArgumentTypes(m);

        String className = AsmUtils.makeExternalName(interfaceClass.getName()) + "$Proxy";
        String methodDesc = Type.getMethodDescriptor(m);
        GeneratorAdapter ga = new GeneratorAdapter(
                cw.visitMethod(Opcodes.ACC_PUBLIC, m.getName(),
                        methodDesc, null, null),
                Opcodes.ACC_PUBLIC, m.getName(),
                Type.getMethodDescriptor(m)
        );

        ga.visitCode();

        // 检查配置是否初始化
        Label initialized = ga.newLabel();
        ga.loadThis();
        ga.getField(Type.getObjectType(className), "$_cfg_" + index, Type.getType(Object.class));
        ga.ifNonNull(initialized);

        // 调用创建方法
        ga.loadThis();
        ga.invokeVirtual(Type.getObjectType(className),
                new org.objectweb.asm.commons.Method("create_" + index, "()Ljava/lang/Object;"));
        ga.pop();

        // 调用代理方法
        ga.mark(initialized);
        ga.loadThis();
        ga.getField(Type.getObjectType(className), "$_obj", PROXY_OBJECT_TYPE);
        ga.loadThis();
        ga.getField(Type.getObjectType(className), "$_cfg_" + index, Type.getType(Object.class));

        // 构建参数数组
        ga.push(paramTypes.length);
        ga.newArray(Type.getType(Object.class));

        for (int i = 0; i < paramTypes.length; i++) {
            ga.dup();
            ga.push(i);
            ga.loadArg(i);
            ga.box(paramTypes[i]);
            ga.arrayStore(Type.getType(Object.class));
        }


        // 调用invoke方法
        ga.invokeInterface(PROXY_OBJECT_TYPE,
                new org.objectweb.asm.commons.Method("invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"));

        // 处理返回值
        AsmUtils.generateReturnObject(ga, returnType);
        ga.endMethod();
    }

    public Class<?> createClass(Class<?> interfaceOrAbstractClass) {
        String className = interfaceOrAbstractClass.getName() + "$Proxy";
        try {
            return Class.forName(className, false, AsmClassloader.get());
        } catch (ClassNotFoundException e) {
            return createImpl(interfaceOrAbstractClass, className);
        }
    }

}
